Andybeyond commited on
Commit
b7e0988
·
verified ·
1 Parent(s): dc918a5

Create notebooks/lyrics_development.ipynb

Browse files

Add lyrics-development base code to start.

Files changed (1) hide show
  1. notebooks/lyrics_development.ipynb +729 -0
notebooks/lyrics_development.ipynb ADDED
@@ -0,0 +1,729 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Lyrics Generation Model Development
2
+ # Author: [Your Name]
3
+ # Project: Opentunes.ai
4
+
5
+ import torch
6
+ import torch.nn as nn
7
+ from transformers import (
8
+ AutoModelForCausalLM,
9
+ AutoTokenizer,
10
+ Trainer,
11
+ TrainingArguments,
12
+ GPT2LMHeadModel,
13
+ GPT2Tokenizer
14
+ )
15
+ import pandas as pd
16
+ import numpy as np
17
+ from pathlib import Path
18
+ import json
19
+ import wandb
20
+ from tqdm import tqdm
21
+
22
+ # 1. Data Loading and Preprocessing
23
+ class LyricsDataset(torch.utils.data.Dataset):
24
+ """
25
+ Custom Dataset for lyrics data.
26
+
27
+ Features:
28
+ - Loads and processes lyrics text
29
+ - Handles style/genre tags
30
+ - Manages rhyme patterns
31
+ - Tokenization for transformer models
32
+ """
33
+
34
+ def __init__(self, data_dir, max_length=512):
35
+ self.data_dir = Path(data_dir)
36
+ self.max_length = max_length
37
+
38
+ # Initialize tokenizer (using GPT-2 as base)
39
+ self.tokenizer = GPT2Tokenizer.from_pretrained('gpt2')
40
+ self.tokenizer.pad_token = self.tokenizer.eos_token
41
+
42
+ # Load lyrics data
43
+ self.lyrics_files = list(self.data_dir.glob("*.txt"))
44
+ self.lyrics_data = self._load_lyrics_data()
45
+
46
+ def _load_lyrics_data(self):
47
+ """Load and preprocess lyrics from files."""
48
+ data = []
49
+ for file in self.lyrics_files:
50
+ with open(file, 'r', encoding='utf-8') as f:
51
+ lyrics = f.read()
52
+
53
+ # Extract metadata from filename or content
54
+ metadata = self._extract_metadata(file)
55
+
56
+ data.append({
57
+ 'lyrics': lyrics,
58
+ 'genre': metadata.get('genre', 'unknown'),
59
+ 'style': metadata.get('style', 'unknown'),
60
+ 'structure': metadata.get('structure', 'verse-chorus')
61
+ })
62
+ return data
63
+
64
+ def _extract_metadata(self, file):
65
+ """Extract metadata from filename or content."""
66
+ # Example filename format: pop_love_verse-chorus.txt
67
+ parts = file.stem.split('_')
68
+ return {
69
+ 'genre': parts[0] if len(parts) > 0 else 'unknown',
70
+ 'style': parts[1] if len(parts) > 1 else 'unknown',
71
+ 'structure': parts[2] if len(parts) > 2 else 'verse-chorus'
72
+ }
73
+
74
+ def __len__(self):
75
+ return len(self.lyrics_data)
76
+
77
+ def __getitem__(self, idx):
78
+ item = self.lyrics_data[idx]
79
+
80
+ # Prepare input text with metadata
81
+ input_text = f"<|genre|>{item['genre']}<|style|>{item['style']}<|lyrics|>{item['lyrics']}"
82
+
83
+ # Tokenize
84
+ encoding = self.tokenizer(
85
+ input_text,
86
+ max_length=self.max_length,
87
+ padding='max_length',
88
+ truncation=True,
89
+ return_tensors='pt'
90
+ )
91
+
92
+ return {
93
+ 'input_ids': encoding['input_ids'].squeeze(),
94
+ 'attention_mask': encoding['attention_mask'].squeeze()
95
+ }
96
+
97
+ # 2. Model Architecture
98
+ class LyricsTransformer(nn.Module):
99
+ """
100
+ Transformer model for lyrics generation.
101
+
102
+ Features:
103
+ - GPT-2 based architecture
104
+ - Style conditioning
105
+ - Rhyme awareness
106
+ - Structure control
107
+ """
108
+
109
+ def __init__(self,
110
+ vocab_size=50257, # GPT-2 vocabulary size
111
+ d_model=768,
112
+ nhead=12,
113
+ num_layers=6):
114
+ super().__init__()
115
+
116
+ # Load pretrained GPT-2
117
+ self.transformer = GPT2LMHeadModel.from_pretrained('gpt2')
118
+
119
+ # Add style embedding
120
+ self.style_embedding = nn.Embedding(100, d_model) # 100 different styles
121
+
122
+ # Add additional layers for style conditioning
123
+ self.style_projection = nn.Linear(d_model, d_model)
124
+ self.genre_embedding = nn.Embedding(50, d_model) # 50 different genres
125
+
126
+ def forward(self, input_ids, attention_mask=None, style_ids=None):
127
+ """
128
+ Forward pass with style conditioning.
129
+
130
+ Args:
131
+ input_ids: Tokenized input text
132
+ attention_mask: Attention mask for padding
133
+ style_ids: Optional style conditioning ids
134
+ """
135
+ # Get base transformer outputs
136
+ outputs = self.transformer(
137
+ input_ids=input_ids,
138
+ attention_mask=attention_mask,
139
+ return_dict=True
140
+ )
141
+
142
+ # Add style conditioning if provided
143
+ if style_ids is not None:
144
+ style_embeds = self.style_embedding(style_ids)
145
+ style_projection = self.style_projection(style_embeds)
146
+ outputs.logits += style_projection
147
+
148
+ return outputs
149
+
150
+ # 3. Training Pipeline
151
+ class LyricsTrainer:
152
+ """
153
+ Training pipeline for lyrics generation model.
154
+
155
+ Features:
156
+ - Custom training loop
157
+ - Style-conditional training
158
+ - Rhyme pattern learning
159
+ - Metrics tracking
160
+ """
161
+
162
+ def __init__(self, model, config, device='cuda'):
163
+ self.model = model.to(device)
164
+ self.config = config
165
+ self.device = device
166
+
167
+ self.optimizer = torch.optim.AdamW(
168
+ model.parameters(),
169
+ lr=config['learning_rate'],
170
+ weight_decay=config.get('weight_decay', 0.01)
171
+ )
172
+
173
+ self.scheduler = torch.optim.lr_scheduler.OneCycleLR(
174
+ self.optimizer,
175
+ max_lr=config['learning_rate'],
176
+ epochs=config['epochs'],
177
+ steps_per_epoch=config['steps_per_epoch']
178
+ )
179
+
180
+ def train_epoch(self, train_loader):
181
+ """Train for one epoch."""
182
+ self.model.train()
183
+ epoch_loss = 0
184
+
185
+ for batch in tqdm(train_loader):
186
+ # Move batch to device
187
+ input_ids = batch['input_ids'].to(self.device)
188
+ attention_mask = batch['attention_mask'].to(self.device)
189
+
190
+ # Forward pass
191
+ outputs = self.model(input_ids, attention_mask=attention_mask)
192
+
193
+ # Calculate loss
194
+ loss = outputs.loss
195
+
196
+ # Backward pass
197
+ self.optimizer.zero_grad()
198
+ loss.backward()
199
+ torch.nn.utils.clip_grad_norm_(self.model.parameters(), 1.0)
200
+ self.optimizer.step()
201
+ self.scheduler.step()
202
+
203
+ epoch_loss += loss.item()
204
+
205
+ return epoch_loss / len(train_loader)
206
+
207
+ def evaluate(self, val_loader):
208
+ """Evaluate the model."""
209
+ self.model.eval()
210
+ val_loss = 0
211
+
212
+ with torch.no_grad():
213
+ for batch in val_loader:
214
+ input_ids = batch['input_ids'].to(self.device)
215
+ attention_mask = batch['attention_mask'].to(self.device)
216
+
217
+ outputs = self.model(input_ids, attention_mask=attention_mask)
218
+ loss = outputs.loss
219
+
220
+ val_loss += loss.item()
221
+
222
+ return val_loss / len(val_loader)
223
+
224
+ # 4. Generation and Inference
225
+ class LyricsGenerator:
226
+ """
227
+ Lyrics generation interface.
228
+
229
+ Features:
230
+ - Prompt-based generation
231
+ - Style control
232
+ - Rhyme scheme enforcement
233
+ - Structure management
234
+ """
235
+
236
+ def __init__(self, model, tokenizer, device='cuda'):
237
+ self.model = model.to(device)
238
+ self.tokenizer = tokenizer
239
+ self.device = device
240
+
241
+ def generate(self, prompt, style=None, max_length=200):
242
+ """Generate lyrics from prompt."""
243
+ self.model.eval()
244
+
245
+ # Prepare input
246
+ input_text = prompt if style is None else f"<|style|>{style}<|prompt|>{prompt}"
247
+ input_ids = self.tokenizer.encode(input_text, return_tensors='pt').to(self.device)
248
+
249
+ # Generate
250
+ outputs = self.model.generate(
251
+ input_ids,
252
+ max_length=max_length,
253
+ num_return_sequences=1,
254
+ no_repeat_ngram_size=3,
255
+ do_sample=True,
256
+ top_k=50,
257
+ top_p=0.95,
258
+ temperature=0.7
259
+ )
260
+
261
+ # Decode
262
+ generated_lyrics = self.tokenizer.decode(outputs[0], skip_special_tokens=True)
263
+
264
+ return generated_lyrics
265
+
266
+ # 5. Evaluation
267
+ class LyricsEvaluator:
268
+ """
269
+ Evaluation metrics for lyrics generation.
270
+
271
+ Metrics:
272
+ - Rhyme quality
273
+ - Syllable count
274
+ - Theme consistency
275
+ - Style adherence
276
+ """
277
+
278
+ def __init__(self, model, tokenizer):
279
+ self.model = model
280
+ self.tokenizer = tokenizer
281
+
282
+ def evaluate_rhyme(self, lyrics):
283
+ """Evaluate rhyme patterns."""
284
+ # Implement rhyme detection and scoring
285
+ pass
286
+
287
+ def evaluate_structure(self, lyrics):
288
+ """Evaluate lyrical structure."""
289
+ # Implement structure analysis
290
+ pass
291
+
292
+ def evaluate_theme(self, lyrics, prompt):
293
+ """Evaluate theme consistency."""
294
+ # Implement theme analysis
295
+ pass
296
+
297
+ # Example Usage
298
+ def main():
299
+ # Load config
300
+ with open('models/lyrics-gen/config/model_config.json') as f:
301
+ config = json.load(f)
302
+
303
+ # Initialize model and tokenizer
304
+ model = LyricsTransformer()
305
+ tokenizer = GPT2Tokenizer.from_pretrained('gpt2')
306
+
307
+ # Create datasets
308
+ train_dataset = LyricsDataset('datasets/lyrics/train')
309
+ val_dataset = LyricsDataset('datasets/lyrics/val')
310
+
311
+ # Initialize trainer
312
+ trainer = LyricsTrainer(model, config)
313
+
314
+ # Train model
315
+ for epoch in range(config['epochs']):
316
+ train_loss = trainer.train_epoch(train_dataset)
317
+ val_loss = trainer.evaluate(val_dataset)
318
+ print(f"Epoch {epoch}: Train Loss = {train_loss:.4f}, Val Loss = {val_loss:.4f}")
319
+
320
+ # Generate sample
321
+ generator = LyricsGenerator(model, tokenizer)
322
+ lyrics = generator.generate(
323
+ prompt="Write a love song about summer",
324
+ style="pop"
325
+ )
326
+ print("Generated Lyrics:", lyrics)
327
+
328
+ if __name__ == "__main__":
329
+ main()
330
+
331
+
332
+
333
+
334
+ # Additional Features for Lyrics Generation
335
+
336
+ # 1. Enhanced Style Control and Structure
337
+ class EnhancedLyricsGenerator(LyricsGenerator):
338
+ """
339
+ Enhanced lyrics generator with advanced features.
340
+
341
+ Additional Features:
342
+ - Song structure management (verse, chorus, bridge)
343
+ - Rhyme scheme control
344
+ - Syllable count management
345
+ - Emotional tone control
346
+ - Genre-specific patterns
347
+ - Metaphor and imagery enhancement
348
+ """
349
+
350
+ def generate_structured_song(self, prompt, structure_dict):
351
+ """
352
+ Generate a complete song with specified structure.
353
+
354
+ Args:
355
+ prompt (str): Main theme/topic
356
+ structure_dict (dict): Song structure specification
357
+ Example:
358
+ {
359
+ 'verse1': {'lines': 4, 'syllables_per_line': 8, 'rhyme_scheme': 'AABB'},
360
+ 'chorus': {'lines': 4, 'syllables_per_line': 6, 'rhyme_scheme': 'ABAB'},
361
+ 'verse2': {'lines': 4, 'syllables_per_line': 8, 'rhyme_scheme': 'AABB'},
362
+ 'bridge': {'lines': 2, 'syllables_per_line': 10, 'rhyme_scheme': 'AA'},
363
+ }
364
+
365
+ Returns:
366
+ dict: Generated song sections with metadata
367
+ """
368
+ song_parts = {}
369
+
370
+ for section, specs in structure_dict.items():
371
+ section_prompt = self._create_section_prompt(
372
+ base_prompt=prompt,
373
+ section_type=section,
374
+ specifications=specs
375
+ )
376
+
377
+ generated_section = self.generate_section(
378
+ prompt=section_prompt,
379
+ rhyme_scheme=specs['rhyme_scheme'],
380
+ syllables=specs['syllables_per_line'],
381
+ num_lines=specs['lines']
382
+ )
383
+
384
+ song_parts[section] = generated_section
385
+
386
+ return self._compile_song(song_parts)
387
+
388
+ def generate_with_emotion(self, prompt, emotion_params):
389
+ """
390
+ Generate lyrics with specific emotional qualities.
391
+
392
+ Args:
393
+ prompt (str): Base prompt
394
+ emotion_params (dict): Emotional parameters
395
+ Example:
396
+ {
397
+ 'primary_emotion': 'joy',
398
+ 'intensity': 0.8,
399
+ 'tone': 'uplifting',
400
+ 'imagery_type': 'nature',
401
+ 'word_choices': 'positive'
402
+ }
403
+ """
404
+ # Enhance prompt with emotional context
405
+ enhanced_prompt = self._add_emotional_context(prompt, emotion_params)
406
+
407
+ # Generate with emotion-specific settings
408
+ return self.generate(
409
+ prompt=enhanced_prompt,
410
+ temperature=self._get_emotion_temperature(emotion_params),
411
+ top_p=self._get_emotion_top_p(emotion_params)
412
+ )
413
+
414
+ def generate_with_metaphors(self, prompt, theme_params):
415
+ """
416
+ Generate lyrics rich in metaphors and imagery.
417
+
418
+ Args:
419
+ prompt (str): Base prompt
420
+ theme_params (dict): Theme and metaphor specifications
421
+ Example:
422
+ {
423
+ 'primary_theme': 'love',
424
+ 'metaphor_source': 'ocean',
425
+ 'imagery_type': 'visual',
426
+ 'complexity_level': 'advanced'
427
+ }
428
+ """
429
+ metaphor_enhanced_prompt = self._enhance_with_metaphors(prompt, theme_params)
430
+ return self.generate(prompt=metaphor_enhanced_prompt)
431
+
432
+ def _enhance_with_metaphors(self, prompt, theme_params):
433
+ """Add metaphorical elements to prompt."""
434
+ metaphor_templates = {
435
+ 'love': {
436
+ 'ocean': [
437
+ "deep as the ocean",
438
+ "waves of emotion",
439
+ "tidal force of feeling"
440
+ ],
441
+ 'fire': [
442
+ "burning passion",
443
+ "flame of desire",
444
+ "scorching intensity"
445
+ ]
446
+ }
447
+ # Add more themes and metaphor sources
448
+ }
449
+
450
+ # Select appropriate metaphors
451
+ chosen_metaphors = self._select_metaphors(
452
+ metaphor_templates,
453
+ theme_params
454
+ )
455
+
456
+ # Enhance prompt with metaphors
457
+ return f"{prompt} {' '.join(chosen_metaphors)}"
458
+
459
+ class RhymeController:
460
+ """
461
+ Controls rhyme patterns in generated lyrics.
462
+
463
+ Features:
464
+ - Multiple rhyme scheme support
465
+ - Syllable counting
466
+ - Assonance detection
467
+ - Alliteration management
468
+ """
469
+
470
+ def __init__(self):
471
+ self.pronunciations = self._load_pronunciations()
472
+
473
+ def enforce_rhyme_scheme(self, lines, scheme):
474
+ """
475
+ Modify lines to follow rhyme scheme.
476
+
477
+ Args:
478
+ lines (list): Generated lines
479
+ scheme (str): Rhyme scheme (e.g., 'AABB', 'ABAB')
480
+
481
+ Returns:
482
+ list: Modified lines with proper rhyming
483
+ """
484
+ rhyme_groups = self._create_rhyme_groups(scheme)
485
+ return self._modify_line_endings(lines, rhyme_groups)
486
+
487
+ def _get_rhyming_words(self, word):
488
+ """Find words that rhyme with given word."""
489
+ pronunciation = self.pronunciations.get(word.lower())
490
+ if not pronunciation:
491
+ return []
492
+
493
+ rhyming_words = []
494
+ for w, p in self.pronunciations.items():
495
+ if self._is_rhyme(pronunciation, p):
496
+ rhyming_words.append(w)
497
+
498
+ return rhyming_words
499
+
500
+ # Advanced Evaluation Metrics
501
+ class EnhancedLyricsEvaluator(LyricsEvaluator):
502
+ """
503
+ Comprehensive evaluation suite for lyrics generation.
504
+
505
+ Metrics Categories:
506
+ 1. Technical Quality
507
+ 2. Musical Compatibility
508
+ 3. Content Analysis
509
+ 4. Style Adherence
510
+ """
511
+
512
+ def evaluate_comprehensive(self, lyrics, target_style=None):
513
+ """
514
+ Run comprehensive evaluation on generated lyrics.
515
+
516
+ Returns:
517
+ dict: Complete evaluation metrics
518
+ """
519
+ return {
520
+ 'technical': self.evaluate_technical(lyrics),
521
+ 'musical': self.evaluate_musical(lyrics),
522
+ 'content': self.evaluate_content(lyrics),
523
+ 'style': self.evaluate_style(lyrics, target_style)
524
+ }
525
+
526
+ def evaluate_technical(self, lyrics):
527
+ """Evaluate technical aspects of lyrics."""
528
+ return {
529
+ 'rhyme_quality': self._analyze_rhyme_patterns(lyrics),
530
+ 'syllable_consistency': self._analyze_syllable_patterns(lyrics),
531
+ 'vocabulary_richness': self._calculate_vocabulary_metrics(lyrics),
532
+ 'grammar_score': self._check_grammar(lyrics)
533
+ }
534
+
535
+ def evaluate_musical(self, lyrics):
536
+ """Evaluate musical compatibility."""
537
+ return {
538
+ 'rhythm_score': self._analyze_rhythm(lyrics),
539
+ 'singability': self._evaluate_singability(lyrics),
540
+ 'phrase_length': self._analyze_phrase_length(lyrics),
541
+ 'stress_patterns': self._analyze_stress_patterns(lyrics)
542
+ }
543
+
544
+ def evaluate_content(self, lyrics):
545
+ """Evaluate lyrical content quality."""
546
+ return {
547
+ 'theme_coherence': self._analyze_theme_consistency(lyrics),
548
+ 'emotional_impact': self._analyze_emotional_content(lyrics),
549
+ 'imagery_score': self._evaluate_imagery(lyrics),
550
+ 'narrative_strength': self._analyze_narrative(lyrics)
551
+ }
552
+
553
+ # Example Usage and Scenarios
554
+
555
+ def demonstrate_lyrics_generation():
556
+ """
557
+ Demonstrate various lyrics generation scenarios.
558
+ """
559
+
560
+ # Initialize generator
561
+ generator = EnhancedLyricsGenerator(model, tokenizer)
562
+
563
+ # 1. Generate a complete pop song
564
+ pop_structure = {
565
+ 'verse1': {
566
+ 'lines': 4,
567
+ 'syllables_per_line': 8,
568
+ 'rhyme_scheme': 'AABB'
569
+ },
570
+ 'chorus': {
571
+ 'lines': 4,
572
+ 'syllables_per_line': 6,
573
+ 'rhyme_scheme': 'ABAB'
574
+ },
575
+ 'verse2': {
576
+ 'lines': 4,
577
+ 'syllables_per_line': 8,
578
+ 'rhyme_scheme': 'AABB'
579
+ }
580
+ }
581
+
582
+ pop_song = generator.generate_structured_song(
583
+ prompt="A summer love story",
584
+ structure_dict=pop_structure
585
+ )
586
+
587
+ # 2. Generate emotional ballad
588
+ emotion_params = {
589
+ 'primary_emotion': 'longing',
590
+ 'intensity': 0.9,
591
+ 'tone': 'melancholic',
592
+ 'imagery_type': 'nature',
593
+ 'word_choices': 'poetic'
594
+ }
595
+
596
+ ballad = generator.generate_with_emotion(
597
+ prompt="Lost love and memories",
598
+ emotion_params=emotion_params
599
+ )
600
+
601
+ # 3. Generate metaphorical lyrics
602
+ theme_params = {
603
+ 'primary_theme': 'love',
604
+ 'metaphor_source': 'ocean',
605
+ 'imagery_type': 'visual',
606
+ 'complexity_level': 'advanced'
607
+ }
608
+
609
+ metaphorical = generator.generate_with_metaphors(
610
+ prompt="Finding inner strength",
611
+ theme_params=theme_params
612
+ )
613
+
614
+ return {
615
+ 'pop_song': pop_song,
616
+ 'ballad': ballad,
617
+ 'metaphorical': metaphorical
618
+ }
619
+
620
+
621
+ # Integration with Melody Generation
622
+ class SongIntegrator:
623
+ """
624
+ Integrates lyrics and melody generation.
625
+
626
+ Features:
627
+ - Synchronizes lyrics with melody
628
+ - Adjusts rhythm to match syllables
629
+ - Ensures musical phrase alignment
630
+ """
631
+
632
+ def __init__(self, lyrics_generator, melody_generator):
633
+ self.lyrics_generator = lyrics_generator
634
+ self.melody_generator = melody_generator
635
+
636
+ def generate_complete_song(self, prompt, style):
637
+ """
638
+ Generate a complete song with matching lyrics and melody.
639
+
640
+ Args:
641
+ prompt (str): Song theme/topic
642
+ style (dict): Musical and lyrical style parameters
643
+
644
+ Returns:
645
+ dict: Complete song with melody and lyrics
646
+ """
647
+ # Generate lyrics first
648
+ lyrics = self.lyrics_generator.generate_structured_song(
649
+ prompt=prompt,
650
+ structure_dict=self._get_structure_for_style(style)
651
+ )
652
+
653
+ # Generate matching melody
654
+ melody = self.melody_generator.generate_with_structure(
655
+ prompt=prompt,
656
+ form=self._extract_form_from_lyrics(lyrics)
657
+ )
658
+
659
+ # Align lyrics and melody
660
+ aligned_song = self._align_lyrics_and_melody(lyrics, melody)
661
+
662
+ return aligned_song
663
+
664
+ def _align_lyrics_and_melody(self, lyrics, melody):
665
+ """Align lyrics with melody phrases."""
666
+ aligned = {}
667
+
668
+ for section in lyrics:
669
+ section_melody = melody[section]
670
+ section_lyrics = lyrics[section]
671
+
672
+ # Adjust melody note durations to match syllables
673
+ adjusted_melody = self._adjust_melody_to_lyrics(
674
+ section_melody,
675
+ self._count_syllables(section_lyrics)
676
+ )
677
+
678
+ aligned[section] = {
679
+ 'lyrics': section_lyrics,
680
+ 'melody': adjusted_melody
681
+ }
682
+
683
+ return aligned
684
+
685
+ def example_complete_song():
686
+ """
687
+ Generate and demonstrate a complete song.
688
+ """
689
+ # Initialize components
690
+ lyrics_gen = EnhancedLyricsGenerator(lyrics_model, tokenizer)
691
+ melody_gen = MelodyGenerator(melody_model)
692
+ integrator = SongIntegrator(lyrics_gen, melody_gen)
693
+
694
+ # Generate complete song
695
+ song = integrator.generate_complete_song(
696
+ prompt="A hopeful song about new beginnings",
697
+ style={
698
+ 'genre': 'pop',
699
+ 'mood': 'uplifting',
700
+ 'tempo': 'moderate',
701
+ 'complexity': 'medium'
702
+ }
703
+ )
704
+
705
+ # Evaluate the result
706
+ evaluator = EnhancedLyricsEvaluator(lyrics_model, tokenizer)
707
+ evaluation = evaluator.evaluate_comprehensive(
708
+ song['lyrics'],
709
+ target_style='pop'
710
+ )
711
+
712
+ return {
713
+ 'song': song,
714
+ 'evaluation': evaluation
715
+ }
716
+
717
+ if __name__ == "__main__":
718
+ # Run demonstrations
719
+ lyrics_examples = demonstrate_lyrics_generation()
720
+ complete_song = example_complete_song()
721
+
722
+ # Print results
723
+ print("Generated Lyrics Examples:")
724
+ print(json.dumps(lyrics_examples, indent=2))
725
+
726
+ print("\nComplete Song Generation:")
727
+ print(json.dumps(complete_song, indent=2))
728
+
729
+